//
//  PullDownRefreshControl.swift
//  YJWeiBo
//
//  Created by Jim1024 on 2017/1/19.
//  Copyright © 2017年 Jim1024. All rights reserved.
//

import UIKit

private var kPullDownRefreshControlKey = "PullDownRefreshControlKey"

// MARK: - 扩展UIScrollView,动态添加下拉刷新控件属性,方便UIScrollView使用下拉刷新
extension UIScrollView {
    /// 计算型属性,计算型属性只是生成getter和setter方法
    fileprivate var pullDuwnRefreshControl: PullDownRefreshControl {
        get {
            if let refreshControl = objc_getAssociatedObject(self, &kPullDownRefreshControlKey) as? PullDownRefreshControl {
                return refreshControl
            } else {
                let refreshControl = PullDownRefreshControl()
                self.addSubview(refreshControl)
                self.pullDuwnRefreshControl = refreshControl
                return refreshControl
            }
        }
        
        set {
            objc_setAssociatedObject(self, &kPullDownRefreshControlKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
    // MARK: - 添加3个对外公开的方法,不让外部直接访问下拉刷新控件属性
    /// 添加开始刷新的闭包
    func addPullDownRefreshHandler(_ handler: @escaping () -> ()) {
        pullDuwnRefreshControl.pullDownRefreshHandler = handler
    }
    
    /// 开始刷新
    func startRefreshing() {
        pullDuwnRefreshControl.startRefreshing()
    }
    
    /// 刷新结束
    func endRefreshing() {
        pullDuwnRefreshControl.endRefreshing()
    }
}


class PullDownRefreshControl: UIView {
    /// 刷新控件的3种状态
    enum PullDownRefreshControlStatus: Int {
        case normal         // 箭头朝下, 下拉刷新
        case pullToRefresh  // 箭头朝上, 释放刷新
        case refreshing     // 正在刷新
    }
    
    fileprivate let pullDownRefreshControlHeight: CGFloat = 60
    /// 刷新控件开始刷新的回调
    var pullDownRefreshHandler: (() -> ())?
    
    /// 父类,定义属性的时候属性类型后面!
    /// UIScrollView!: 表示可选类型,隐式拆包,在使用的时候可以不用强制拆包,需要保证在使用的时候一定有值.如果值为nil,也是会挂掉
    /// 和?有区别.UIScrollView?,后面使用需要强制拆包
    /// UIScrollView!: 后面使用不需要强制拆包.如果值为nil,也是会挂掉
    fileprivate weak var scrollView: UIScrollView!
    
    /// 记录上一次的状态
    fileprivate var oldStatus: PullDownRefreshControlStatus = .normal
    /// 刷新控件的当前刷新状态,默认是 Normal:箭头朝下, 下拉刷新
    fileprivate var currentStatus: PullDownRefreshControlStatus = .normal {
        didSet {
            switch currentStatus {
            case .normal:
                loadingView.layer.removeAllAnimations()
                arrowView.isHidden = false
                loadingView.isHidden = true
                messageLabel.text = "下拉刷新"
                
                UIView.animate(withDuration: YJDefaultDuration, animations: { 
                    self.arrowView.transform = CGAffineTransform.identity
                })
                
                // 只有上次是刷新状态,再切换到Normal状态的时候,才让tableView往上走刷新控件的高度,把刷新控件隐藏掉
                if oldStatus == .refreshing {
                    UIView.animate(withDuration: YJDefaultDuration, animations: { 
                        self.scrollView.contentInset.top -= self.pullDownRefreshControlHeight
                    })
                }
            case .pullToRefresh:
                // 箭头旋转,文字变为: 释放刷新
                messageLabel.text = "释放刷新"
                UIView.animate(withDuration: YJDefaultDuration, animations: { 
                    self.arrowView.transform = CGAffineTransform(rotationAngle: CGFloat.pi)
                })
            case .refreshing:
                // 箭头隐藏,文字变为: 正在刷新
                arrowView.isHidden = true
                loadingView.isHidden = false
                messageLabel.text = "正在刷新..."
                
                let rotation = CABasicAnimation(keyPath: "transform.rotation")
                rotation.toValue = M_PI * 2
                rotation.duration = 0.5
                rotation.repeatCount = MAXFLOAT
                
                // forKey:如果传nil,动画叠加, forKey:不等于nil,重新动画
                loadingView.layer.add(rotation, forKey: "rotation")
                
                // 修改scrollView.contentInset.top的值,让tableView不要滚动到原来的位置,需要在往下移动刷新控件的高度,就能显示刷新控件
                UIView.animate(withDuration: YJDefaultDuration, animations: { 
                    self.scrollView.contentInset.top += self.pullDownRefreshControlHeight
                }, completion: { (_) in
                    // 刷新控件动画到导航栏下方的时候调用
                    self.pullDownRefreshHandler?()
                })
            }
            oldStatus = currentStatus
        }
    }
    
    override init(frame: CGRect) {
        let newFrame = CGRect(x: 0, y: -pullDownRefreshControlHeight, width: UIScreen.main.bounds.width, height: pullDownRefreshControlHeight)
        super.init(frame: newFrame)
        setupUI()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    deinit {
        scrollView.removeObserver(self, forKeyPath: "contentOffset")
    }
    
    /// view即将被添加到父控件上面,当调用addSubView后会触发
    /// newSuperview: 父类
    override func willMove(toSuperview newSuperview: UIView?) {
        super.willMove(toSuperview: newSuperview)
        
        // KVO,监听父类对象属性的改变tableView.contentOffest
        if let scrollView = newSuperview as? UIScrollView {
            self.scrollView = scrollView
            scrollView.addObserver(self, forKeyPath: "contentOffset", options: [NSKeyValueObservingOptions.old, NSKeyValueObservingOptions.new], context: nil)
        }
    }
    
    /// KVO.当监听对象的属性发生改变的时候回调用
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if scrollView.isDragging { // scrollView正在处于手指推动状态
            if currentStatus == .normal && scrollView.contentOffset.y < -scrollView.contentInset.top - pullDownRefreshControlHeight {
                JPrint("切换到PullToRefresh")
                currentStatus = .pullToRefresh
            } else if currentStatus == .pullToRefresh && scrollView.contentOffset.y > -scrollView.contentInset.top - pullDownRefreshControlHeight {
                JPrint("切换到Normal")
                currentStatus = .normal
            }
        } else if currentStatus == .pullToRefresh {
            // 手指松开
            // 当松手的时候,如果是PullToRefresh状态就切换到Refreshing,正在刷新,手指推动是没有反应的
            JPrint("切换到Refreshing")
            currentStatus = .refreshing
        }
    }
    
    /// 开始刷新
    fileprivate func startRefreshing() {
        currentStatus = .refreshing
    }
    
    /// 结束刷新,加载到数据后来调用
    fileprivate func endRefreshing() {
        currentStatus = .normal
    }
    
    fileprivate func setupUI() {
        backgroundColor = UIColor.clear
        
        addSubview(arrowView)
        addSubview(loadingView)
        addSubview(messageLabel)
        
        /*================= layout =================*/
        
        arrowView.translatesAutoresizingMaskIntoConstraints = false
        loadingView.translatesAutoresizingMaskIntoConstraints = false
        messageLabel.translatesAutoresizingMaskIntoConstraints = false
        
        // 箭头
        addConstraint(NSLayoutConstraint(item: arrowView, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: self, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: -30))
        addConstraint(NSLayoutConstraint(item: arrowView, attribute: NSLayoutAttribute.centerY, relatedBy: NSLayoutRelation.equal, toItem: self, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 0))
        
        // loadingView
        addConstraint(NSLayoutConstraint(item: loadingView, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: arrowView, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0))
        addConstraint(NSLayoutConstraint(item: loadingView, attribute: NSLayoutAttribute.centerY, relatedBy: NSLayoutRelation.equal, toItem: self, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 0))
        
        // messageLabel
        addConstraint(NSLayoutConstraint(item: messageLabel, attribute: NSLayoutAttribute.leading, relatedBy: NSLayoutRelation.equal, toItem: arrowView, attribute: NSLayoutAttribute.trailing, multiplier: 1, constant: 10))
        addConstraint(NSLayoutConstraint(item: messageLabel, attribute: NSLayoutAttribute.centerY, relatedBy: NSLayoutRelation.equal, toItem: arrowView, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 0))
    }
    
    // MARK: - 懒加载
    /// 箭头
    fileprivate lazy var arrowView: UIImageView = UIImageView(image: UIImage(named: "tableview_pull_refresh"))
    
    /// loadingView
    fileprivate lazy var loadingView: UIImageView = UIImageView(image: UIImage(named: "tableview_loading"))
    
    fileprivate lazy var messageLabel: UILabel = {
        let label = UILabel()
        
        label.textColor = UIColor.darkGray
        label.font = UIFont.systemFont(ofSize: 14)
        label.text = "下拉刷新"
        
        return label
    }()
}
